package com.chwl.framework.grpc.service;

import com.chwl.framework.grpc.annotation.GrpcService;
import com.chwl.framework.grpc.exception.UserdefinedException;
import com.chwl.framework.grpc.utils.ProtobufUtils;
import com.chwl.framework.rpc.CommonServiceGrpc;
import com.google.protobuf.ByteString;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;


public class GrpcCommonService extends CommonServiceGrpc.CommonServiceImplBase {

    private static final Logger logger = LoggerFactory.getLogger(GrpcCommonService.class);
    Class[] classes;
    HashMap<String, Class> hashMap = null;
    HashMap<String, Object> objectHashMap = null;

    public Class[] getClasses() {
        return classes;
    }

    public void setClasses(Class[] classes) {
        this.classes = classes;
    }

    public Set<String> getServiceNames() {
        return hashMap.keySet();
    }


    public GrpcCommonService(Class... classes) throws UserdefinedException {
        try {
            this.classes = classes;
            hashMap = new HashMap<String, Class>(classes.length);
            objectHashMap = new HashMap<String, Object>(classes.length);

            for (Class cla : classes) {
                Annotation annotation = cla.getAnnotation(GrpcService.class);
                if (annotation == null) {
                    logger.error("common service,init not found GrpcService annotation,class:{}", cla.getName());
                    continue;
                }
                GrpcService grpcService = (GrpcService) annotation;
                String serverName = grpcService.server();
                objectHashMap.put(serverName, cla.newInstance());
                hashMap.put(serverName, cla);
            }
        } catch (Throwable e) {
            logger.error("common service,init commonService fail,error message:{}", e.getMessage(), e);
            throw new UserdefinedException("init commonService fail,error message:" + e.getMessage());
        }
    }

    private Method getMethod(Class cla, String methodName) {
        Method[] methods = cla.getMethods();
        for (Method object : methods) {
            if (object.getName().equals(methodName)) {
                return object;
            }
        }
        return null;
    }

    @Override
    public void handle(com.chwl.framework.rpc.GrpcService.Request request, StreamObserver<com.chwl.framework.rpc.GrpcService.Response> responseObserver) {
        GrpcRequest grpcRequest = ProtobufUtils.deserialize(request.getRequest().toByteArray(), GrpcRequest.class);
        GrpcResponse response = new GrpcResponse();
        String beanName = grpcRequest.getBeanName();
        String methodName = grpcRequest.getMethodName();
        Object[] args = grpcRequest.getArgs();
        try {
            Class cla = hashMap.get(beanName);
            Method method = getMethod(cla, methodName);
            Object result = method.invoke(getBean(beanName), args);
            response.success(result);
        } catch (Exception e) {
            logger.error("common service,handle fail,error message:{}", e.getMessage(), e);
            response.error("service bean '" + beanName + "' not found.");
        }
        ByteString bytes = ByteString.copyFrom(ProtobufUtils.serialize(response));
        com.chwl.framework.rpc.GrpcService.Response grpcResponse = com.chwl.framework.rpc.GrpcService.Response.newBuilder().setReponse(bytes).build();
        responseObserver.onNext(grpcResponse);
        responseObserver.onCompleted();
    }

    /**
     * 获取 Service Bean
     */
    private Object getBean(String beanName) {

        return objectHashMap.get(beanName);
    }
}
